/*
    Copyright 2006 Andrew Wilkinson <andrew@indiegigs.co.uk>

    This file is part of GL2x.

    GL2x is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    GL2x is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GL2x; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "driver.h"

#include "stdio.h"

#define vertex(i) poly->vertices[i].v

GL2x_triangle_info tri_info;

static inline Vector project(const Matrix mv, const Matrix pj, const Vector v) {
    Vector r = Matrix_mulv(mv, v);
    r = Matrix_mulv(pj, r);
    r.x = Fixed_add(Fixed_mul(Fixed_add(r.x, Fixed_one), viewport_half_width), viewport_x);
    r.y = Fixed_add(Fixed_mul(Fixed_add(r.y, Fixed_one), viewport_half_height), viewport_y);
    return r;
}

void GL2x_render(struct glpoly* poly) {
    switch(poly->mode) {
    case GL_POINTS:
        {
        int i, x, y, r, g, b, a, ps, mx, my;
        Vector v;

        ps = Fixed_round(Fixed_divi(point_size, 2));

        for(i = 0; i < poly->count; i++) {
            v = project(poly->modelview, poly->proj, poly->vertices[i]);

            r = Fixed_round(Fixed_mul(poly->colours[i].r, Fixed_createi(255)));
            g = Fixed_round(Fixed_mul(poly->colours[i].g, Fixed_createi(255)));
            b = Fixed_round(Fixed_mul(poly->colours[i].b, Fixed_createi(255)));
            a = Fixed_round(Fixed_mul(poly->colours[i].a, Fixed_createi(255)));

            mx = Fixed_round(Fixed_addi(v.x, ps));
            my = Fixed_round(Fixed_addi(v.y, ps));

            for(x=Fixed_round(Fixed_subi(v.x, ps)); x < mx; x++) {
                for(y=Fixed_round(Fixed_subi(v.y, ps)); y < my; y++) {
                    GP2x_putPixel(x, y, r, g, b, a);
                }
            }
        }
        }
        break;
    case GL_LINES:
        {
        int i;
        Vector v1;
        Vector v2;

        for(i = 0; i < poly->count; i+=2) {
            v1 = project(poly->modelview, poly->proj, poly->vertices[i]);
            v2 = project(poly->modelview, poly->proj, poly->vertices[i+1]);

            if(shade_model == GL_FLAT) {
                GL2x_renderline(v1, v2, poly->colours[i+1], poly->colours[i+1]);
            } else {
                GL2x_renderline(v1, v2, poly->colours[i+1], poly->colours[i]);
            }

        }

        }
        break;
    case GL_LINE_STRIP:
        {
        int i;
        Vector v1;
        Vector v2;

        v1 = project(poly->modelview, poly->proj, poly->vertices[0]);
        for(i = 1; i < poly->count; i++) {
            v2 = project(poly->modelview, poly->proj, poly->vertices[i]);

            if(shade_model == GL_FLAT) {
                GL2x_renderline(v1, v2, poly->colours[i+1], poly->colours[i+1]);
            } else {
                GL2x_renderline(v1, v2, poly->colours[i+1], poly->colours[i]);
            }

            v1 = v2;
        }

        }
        break;
    case GL_LINE_LOOP:
        {
        }
        break;
    case GL_TRIANGLES: /* 4 */
        {
        int i;

        for(i = 0; i < poly->count; i+=3) {
            tri_info.v1 = project(poly->modelview, poly->proj, poly->vertices[i]);
            tri_info.c1 = poly->colours[i];
            tri_info.v2 = project(poly->modelview, poly->proj, poly->vertices[i+1]);
            tri_info.c2 = poly->colours[i+1];
            tri_info.v3 = project(poly->modelview, poly->proj, poly->vertices[i+2]);
            tri_info.c3 = poly->colours[i+2];

            #include "rendercore.c"
        }
        }
        break;
    case GL_TRIANGLE_STRIP: /* 5 */
        {
        int i;

        tri_info.v1 = project(poly->modelview, poly->proj, ((Vector*)GP2x_convert_ptr_from(poly->vertices))[0]);
        tri_info.c1 = ((Vector*)GP2x_convert_ptr_from(poly->colours))[0];
        tri_info.v2 = project(poly->modelview, poly->proj, ((Vector*)GP2x_convert_ptr_from(poly->vertices))[1]);
        tri_info.c2 = ((Vector*)GP2x_convert_ptr_from(poly->colours))[1];
        for(i = 2; i < poly->count; i++) {
            tri_info.v3 = project(poly->modelview, poly->proj, ((Vector*)GP2x_convert_ptr_from(poly->vertices))[i]);
            tri_info.c3 = ((Vector*)GP2x_convert_ptr_from(poly->colours))[i];

            #include "rendercore.c"

            tri_info.v1 = tri_info.v2; tri_info.v2 = tri_info.v3;
            tri_info.c1 = tri_info.c2; tri_info.c2 = tri_info.c3;
        }
        }
        break;
    case GL_TRIANGLE_FAN: /* 6 */
        {
        int i;
        GL2x_triangle_info tri_info;

        tri_info.v1 = project(poly->modelview, poly->proj, poly->vertices[0]);
        tri_info.c1 = poly->colours[0];
        tri_info.v2 = project(poly->modelview, poly->proj, poly->vertices[1]);
        tri_info.c2 = poly->colours[1];
        for(i = 2; i < poly->count; i++) {
            tri_info.v3 = project(poly->modelview, poly->proj, poly->vertices[i]);
            tri_info.c3 = poly->colours[1];

            #include "rendercore.c"

            tri_info.v2 = tri_info.v3;
            tri_info.c2 = tri_info.c3;
        }
        }
        break;
    case GL_QUADS: /* 7 */
        {
        int i;
        Vector _v1;
        Vector _v2;
        Vector _v3;
        Vector _v4;
        GL2x_triangle_info tri_info;

        for(i = 0; i < poly->count-3; i+=4) {
            _v1 = project(poly->modelview, poly->proj, poly->vertices[i]);
            _v2 = project(poly->modelview, poly->proj, poly->vertices[i+1]);
            _v3 = project(poly->modelview, poly->proj, poly->vertices[i+2]);
            _v4 = project(poly->modelview, poly->proj, poly->vertices[i+3]);

            tri_info.v1 = _v1;
            tri_info.v2 = _v2;
            tri_info.v3 = _v3;
            tri_info.c1 = poly->colours[i];
            tri_info.c2 = poly->colours[i+1];
            tri_info.c3 = poly->colours[i+2];
            #include "rendercore.c"

            tri_info.v1 = _v2;
            tri_info.v2 = _v3;
            tri_info.v3 = _v4;
            tri_info.c1 = poly->colours[i+1];
            tri_info.c2 = poly->colours[i+2];
            tri_info.c3 = poly->colours[i+3];
            #include "rendercore.c"
        }
        }
        break;
    case GL_POLYGON: /* 9 */
        { /*BUG: this is completely wrong and needs to be rewriten */
        int i;
        GL2x_triangle_info tri_info;

        tri_info.v1 = project(poly->modelview, poly->proj, poly->vertices[0]);
        tri_info.c1 = poly->colours[0];
        tri_info.v2 = project(poly->modelview, poly->proj, poly->vertices[1]);
        tri_info.c1 = poly->colours[1];
        for(i = 2; i < poly->count; i++) {
            tri_info.v3 = project(poly->modelview, poly->proj, poly->vertices[i]);
            tri_info.c1 = poly->colours[i];

            #include "rendercore.c"

            tri_info.v1 = tri_info.v2; tri_info.v2 = tri_info.v3;
            tri_info.c1 = tri_info.c2; tri_info.c2 = tri_info.c3;
        }
        }
        break;
    default:
        printf("Error, unable to render polygon type %i.\n", poly->mode);
    }
}
